package XNAT::DICOM::Entity;
use strict;
use Carp qw(croak carp cluck confess);

=head1 XNAT::DICOM::Entity

A class with common methods for XNAT::DICOM entities.

=over 4

=item $series->addWarnings(@warnings)

Add some warnings associated with this DICOM File. Usually not used by the user.

=cut

sub addWarnings {
  my $self = shift;
	my @warnings = @_;
  $self->{_warnings_hash} = {} unless $self->{_warnings_hash};
  $self->{_warnings_count} = 0 unless $self->{_warnings_count};
	$self->debug(4,"${self}->addWarnings(@_)\n");
  foreach my $warning_string (@warnings) {
    chomp $warning_string;
    $self->{_warnings_hash}->{$warning_string} = 0 unless $self->{_warnings_hash}->{$warning_string};
    $self->{_warnings_hash}->{$warning_string}++;
    $self->{_warnings_count} ++;
		$self->debug(3,"Adding Warning ($self): $warning_string ($self->{_warnings_hash}->{$warning_string} of $self->{_warnings_count})\n");
  }
}

=item $series->addErrors(@errors)

Add some errors associated with this DICOM File. Usually not used by the user.

=cut

sub addErrors {
  my $self = shift;
	my @errors = shift;
  $self->{_errors_hash} = {} unless $self->{_errors_hash};
  $self->{_errors_count} = 0 unless $self->{_errors_count};
	$self->debug(3,"DEBUG: ${self}->addErrors(@_)\n");
  foreach my $error_string ( @errors) {
    chomp $error_string;
    $self->{_errors_hash}->{$error_string} = 0 unless $self->{_errors_hash}->{$error_string};
    $self->{_errors_hash}->{$error_string}++;
    $self->{_errors_count} ++;
		$self->debug(3,"Adding Error ($self): $error_string ($self->{_errors_hash}->{$error_string} of $self->{_errors_count})\n");
  }
}

=item $series->hadWarnings()

True if there were warnings, false otherwise. Faster than $series->getWarnings, so use it instead of that to check first.

=cut

sub hadWarnings {
	my $self = shift;
	$self->_update_warnings_and_errors;
	$self->{_warnings_count} || 0;
}

=item $series->hadErrors()

True if there were errors, false otherwise. Faster than $series->getErrors, so use it instead of that to check first.

=cut

sub hadErrors {
	my $self = shift;
	$self->_update_warnings_and_errors;
	$self->{_errors_count} || 0;
}

sub _update_warnings_and_errors {
	my $self = shift;
	if($self->_get_need_to_update) {
		my $warning_count = 0;
		my $errors_count = 0;
		foreach my $child ( $self->getChildren() ) {
			$warning_count += $child->hadWarnings;
			$errors_count += $child->hadErrors;
		}
		$self->{_warnings_count} = $warning_count + (keys %{$self->{_warnings_hash}});
		$self->{_errors_count} = $errors_count + (keys %{$self->{_errors_hash}});
		$self->_unset_need_to_update;
	}
}

=item $series->getWarnings()

Get a list of warnings. Returns a list of strings. Gets recomputed every time, so check first with $series->hadWarnings before calling.

=cut

sub getWarnings {
  my $self = shift;
	my $indent = shift || "";
	$self->debug(4,"Checking $self $self->{label} for warnings.\n");
  my @warnings_keys = (sort keys %{$self->{_warnings_hash}});
  my @warnings;

	$self->debug(3,"getWarnings ($self) warning count='".(@warnings_keys+0)."'\n");

  foreach my $warning_string (@warnings_keys) {
    if(1 == $self->{_warnings_hash}->{$warning_string}) {
			$self->debug(3,"getWarnings ($self): $warning_string\n");
      push @warnings, $indent."WARNING: $warning_string\n";
    } else {
      push @warnings, $indent."WARNING: $warning_string ($self->{_warnings_hash}->{$warning_string} times).\n";
			$self->debug(3,"getWarnings ($self): $warning_string\n");
    }
  }
	$self->debug(4, " - $self has ". $self->getChildren() . " children.\n");
	foreach my $child ( $self->getChildren() ) {
		$self->debug(4,"Checking child $child->{label} for warnings.\n");
		if($child->hadWarnings) {
			push @warnings, $indent.$child->{label}." Had ".$child->hadWarnings()." Warnings:\n";
			push @warnings, $child->getWarnings($indent."  ");
		}
	}
	@warnings;
}

=item $series->getErrors()

Get a list of errors. Returns a list of strings. Gets recomputed every time, so check first with $series->hadErrors before calling.

=cut

sub getErrors {
  my $self = shift;
	my $indent = shift || "";
  my @errors_keys = (sort keys %{$self->{_errors_hash}});
  my @errors;

	$self->debug(3,"getErrors ($self) error count='".(@errors_keys+0)."'\n");

  foreach my $error_string (@errors_keys) {
    if(1 == $self->{_errors_hash}->{$error_string}) {
			$self->debug(3,"getErrors ($self): $error_string\n");
      push @errors, $indent."ERROR: $error_string\n";
    } else {
      push @errors, $indent."ERROR: $error_string ($self->{_errors_hash}->{$error_string} times).\n";
			$self->debug(3,"getErrors ($self): $error_string\n");
    }
  }
	foreach my $child ( $self->getChildren() ) {
		if($child->hadErrors) {
			push @errors, $indent.$child->{label}." Had ".$child->hadErrors()." Errors:\n";
			push @errors, $child->getErrors($indent."  ");
		}
	}
	@errors;
}

sub _set_need_to_update { $_[0]->{need_to_update} = 1; }
sub _unset_need_to_update { $_[0]->{need_to_update} = 0; }
sub _get_need_to_update { $_[0]->{need_to_update}; }
sub addChild {
	my $self = shift;
	my $child = shift;
	confess("Wrong number of arguments!\n") if @_;
	$self->{_entity_children} = [] unless $self->{_entity_children};
	$self->debug(3,"addChild '$child->{label}' to '$self->{label}'\n");
	if($child->{_entity_parent}) {
		confess "FATAL ERROR: addChild child '$child->{label}' already has a parent.\n";
	}
	$child->{_entity_parent} = $self;
	push @{$self->{_entity_children}}, $child;
	$self->_set_need_to_update;
	$self->{_entity_parent}->_set_need_to_update if $self->{_entity_parent};
}
sub getChildren {
	my $self = shift;
	$self->{_entity_children} = [] unless $self->{_entity_children};
	@{$self->{_entity_children}};
}

sub pretty_seconds {
	my $self = shift;
  my $seconds = shift;
  if ($seconds < 0.001) {
    sprintf('%0.2f microseconds', ( $seconds * 1_000_000 ));
  } elsif ($seconds < 0.25) {
    sprintf('%0.2f milliseconds', ( $seconds * 1_000 ));
  } elsif ($seconds < 60) {
    sprintf('%0.2f seconds', ( $seconds));
  } elsif ($seconds < 3_600) {
    sprintf('%0.2f minutes', ( $seconds / 60 ));
  } elsif ($seconds < 86_400 ) {
    sprintf('%0.2f hours', ( $seconds / 3_600 ));
  } elsif ($seconds < 1) {
    sprintf('%0.2f days', ( $seconds * 86_400 ));
  }
}

sub debug {
	my $self = shift;
	my $level = shift || 1;
	if($main::verbose > $level) {
		foreach(@_) {
			print "DEBUG: ",$_;
		}
	}
}
sub info {
	my $self = shift;
	if($main::verbose > 1) {
		foreach(@_) {
			print "INFO: ",$_;
		}
	}
}


=back

=cut

1;

__END__
